Visual Studio 2003を使用し、Managed C++で記述したManaged/UnmanagedのMixedコードをNUnitでテストすると失敗するケースがあります。
具体的には、GC対象ではないクラスに対するnewや、昔懐かしいmallocで発生することを確認しました。
再現コードを以下に示します。
再現コード §
Visual Studio 2003を使用し、C++のWin32、DLLのプロジェクトを作成します。
プロジェクトのプロパティからマネージ拡張を「はい」に変更します。
参照にnunit.framework.dllを追加します。
その後、メインソースコードとなるcppファイルを以下のように記述します。
#include "stdafx.h"
#using <mscorlib.dll>
#include <malloc.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
using namespace System;
using namespace NUnit::Framework;
namespace NUnitAllocFailTest
{
class NonGCSample
{
};
[TestFixture]
public __gc class TestSample
{
public:
[Test] void Test1()
{
NonGCSample * p = new NonGCSample();
}
[Test] void Test2()
{
void * p = malloc(100);
}
};
}
これをビルドし、NUnit 2.0のNUnit-GUIで実行すると例外で落ちます。
Visual Studio.NET 2003内でTest Driven.NETを用いてテスト実行すると以下のような結果を得ます。
------ Test started: Assembly: NUnitAlloc002.dll ------
TestCase 'NUnitAllocFailTest.TestSample.Test1' failed: System.NullReferenceException : オブジェクト参照がオブジェクト インスタンスに設定されていません。
at new(UInt32 )
d:\w\test\nunitalloc002\nunitalloc002.cpp(46,0): at NUnitAllocFailTest.TestSample.Test1()
TestCase 'NUnitAllocFailTest.TestSample.Test2' failed: System.NullReferenceException : オブジェクト参照がオブジェクト インスタンスに設定されていません。
at malloc(UInt32 )
d:\w\test\nunitalloc002\nunitalloc002.cpp(50,0): at NUnitAllocFailTest.TestSample.Test2()
0 succeeded, 2 failed, 0 skipped, took 0.09 seconds.
---------------------- Done ----------------------
原因 §
動作を追いかけてみましたが、どうもnewの下請け関数からクリティカルセクションに入るあたりに問題があるような気がしますが、定かではありません。厳密には追求していません。
安易な解決 §
とりあえず、安易ではありますが、以下のようにすればnewの問題は解決します。
Win32 APIのLocalAlloc呼び出しが問題ないことは確認済みなので、以下のコードを追加して、newとdeleteをオーバーライドします。
void * operator new( size_t stAllocateBlock )
{
void *pvTemp = (void*)LocalAlloc( LMEM_FIXED, stAllocateBlock );
/* えっと、以下の2行はいるのかな? サンプルソースから安易に引いてきたけれど */
if( pvTemp != 0 )
memset( pvTemp, 0, stAllocateBlock );
return pvTemp;
}
void operator delete( void *pvMem )
{
LocalFree( (HLOCAL)pvMem );
}
しかし、この方法でもmallocの問題は解決しません。
もし、mallocを使う必要があるなら、malloc/freeもユーザープログラム側で置き換えて使う必要があるかもしれません。
未完 §
この内容は未完です。これが完全な解決であるとか、問題の解消というわけではありません。
関連する情報等は歓迎します。